<!--
  @desc可拖拽移动的 dialog
  @author 石艳军
  @date 2022-06-17
  使用方式:
    <gis-drag-dialog :title="title" :visible.sync="dialogVisible" width="620" append-to-body></gis-drag-dialog>
-->
<template>
  <transition name="fade">
    <div
      v-show="visible"
      ref="dragDialogRef"
      v-dragDialog
      class="gis-drag-dialog"
      :class="{
        medium: Number(width) > 700 && Number(width) <= 850,
        small: Number(width) > 400 && Number(width) <= 700,
        mini: Number(width) > 260 && Number(width) <= 400,
        micro: Number(width) <= 260
      }"
      :style="{ width: width + 'px', left: left, top: top, right: right, bottom: bottom }"
    >
      <div class="drag-dialog__header drag-header">
        <span class="header-title">{{title}}</span>
        <span class="header-close-icon" @click="onClose($event)"></span>
      </div>

      <div class="drag-dialog__body" :style="{ padding: bodyPadding }">
        <slot></slot>
      </div>

      <div v-if="$slots.footer" slot="footer" class="dialog-footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  name: 'gis-drag-dialog',
  props: {
    title: {
      type: String,
      default: '',
    },
    visible: {
      type: Boolean,
      default: false,
    },
    appendToBody: {
      type: Boolean,
      default: false,
    },
    beforeClose: {
      type: Function,
    },
    width: {
      type: [Number, String],
      default: 400,
    },
    // 距离头部距离, default: 80
    topGap: {
      type: Number,
      default: 0,
    },
    left: {
      type: String,
      default: '',
    },
    top: {
      type: String,
      default: '',
    },
    right: {
      type: String,
      default: '',
    },
    bottom: {
      type: String,
      default: '',
    },
    bodyPadding: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      // topGap: 0,  // 距离头部距离, default: 80
    };
  },
  watch: {
    visible: {
      handler(bool) {
        if (bool) {
          this.$emit('open', this.$props);
          if (this.appendToBody) {
            this.$nextTick(() => {
              document.body.appendChild(this.$refs.dragDialogRef);
            });
          }
        }
      },
      immediate: true,
    },
  },
  created() {
    document.addEventListener('keyup', this.handleKeyup);
  },
  beforeDestroy() {
    this.$refs.dragDialogRef.remove();
    document.removeEventListener('keyup', this.handleKeyup);
  },
  methods: {
    handleKeyup(e) {
      // 按 esc 关闭弹框
      if (e.keyCode === 27) {
        this.onClose();
      }
    },

    /**
     * 关闭弹框
     */
    onClose() {
      if (this.$props.beforeClose) {
        this.$props.beforeClose(() => {
          this.$emit('close');
          this.$emit('update:visible', false);
        });
      } else {
        this.$emit('close');
        this.$emit('update:visible', false);
      }
    },
  },
  directives: {
    dragDialog: {
      bind(el, binding, vnode, oldVnode) {
        const dialogHeaderEl = el.querySelector('.drag-header');
        const dragDom = el;
        // dialogHeaderEl.style.cursor = 'move';
        dialogHeaderEl.style.cssText += ';cursor:move;';
        dragDom.style.cssText += ';z-index:99;';

        // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
        const sty = (function () {
          if (window.document.currentStyle) {
            return (dom, attr) => dom.currentStyle[attr];
          } else {
            return (dom, attr) => getComputedStyle(dom, false)[attr];
          }
        })();

        dialogHeaderEl.onmousedown = (e) => {
          // 鼠标按下，计算当前元素距离可视区的距离
          const disX = e.clientX - dialogHeaderEl.offsetLeft;
          const disY = e.clientY - dialogHeaderEl.offsetTop;

          const screenWidth = document.body.clientWidth; // body当前宽度
          const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度，可某些环境下无法获取)

          const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
          const dragDomheight = dragDom.offsetHeight; // 对话框高度

          const minDragDomLeft = dragDom.offsetLeft;
          const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;

          const minDragDomTop = dragDom.offsetTop - vnode.context.topGap;
          const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight - vnode.context.topGap;

          // 获取到的值带px 正则匹配替换
          let styL = sty(dragDom, 'left');
          let styT = sty(dragDom, 'top');

          // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
          if (styL.includes('%')) {
            styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
            styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
          } else {
            styL = +styL.replace(/\px/g, '');
            styT = +styT.replace(/\px/g, '');
          }

          document.onmousemove = function (e) {
            // 通过事件委托，计算移动的距离
            let left = e.clientX - disX;
            let top = e.clientY - disY;

            // 边界处理
            if (-left > minDragDomLeft) {
              left = -minDragDomLeft;
            } else if (left > maxDragDomLeft) {
              left = maxDragDomLeft;
            }

            if (-top > minDragDomTop) {
              top = -minDragDomTop;
            } else if (top > maxDragDomTop) {
              top = maxDragDomTop;
            }

            // 移动当前元素
            dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
          };

          document.onmouseup = function (e) {
            document.onmousemove = null;
            document.onmouseup = null;
          };
          return false;
        };
      },
    },
  },
};
</script>

<style lang="scss" scoped>
$headerHeight: 46px;

.gis-drag-dialog {
  position: absolute;
  border-radius: 5px;
  z-index: 9999;
  box-shadow: 0 0 5px 0;
  background: rgba(3, 16, 33, 0.85);
  border: 1px solid rgba(0, 172, 252, 0.4);

  &::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translate(-50%, 50%);
    background-repeat: no-repeat;
    background-position: center 0;
    width: 60%;
    height: 18px;
  }

  &.mini {
    &::after {
      background-image: url("images/wrap_bottom_mini.png");
    }

    .drag-dialog__header {
      background-image: url("images/wrap_title_mini.png");
    }
  }

  &.micro {
    &::after {
      background-image: url("images/wrap_bottom_mini.png");
    }

    .drag-dialog__header {
      background-image: url("images/wrap_title_micro.png");
    }
  }

  &.small {
    &::after {
      background-image: url("images/wrap_bottom_small.png");
    }

    .drag-dialog__header {
      background-image: url("images/wrap_title_small.png");
    }
  }

  &.medium {
    &::after {
      background-image: url("images/wrap_bottom_medium.png");
    }

    .drag-dialog__header {
      background-image: url("images/wrap_title_medium.png");
    }
  }

  .drag-dialog__header {
    width: calc(100% - 36px);
    height: $headerHeight;
    line-height: $headerHeight;
    padding-top: 6px;
    padding-left: 14px;
    background-color: transparent;
    background-repeat: no-repeat;
    background-size: auto 100%;
    position: relative;

    .header-title {
      background-image: url("images/wrap_title_arrow.png");
      background-repeat: no-repeat;
      background-position: 0 center;
      position: absolute;
      top: 0;
      z-index: 100;
      padding-left: 28px;
      color: #fff;
      font-weight: bold;
      font-size: 16px;
    }

    .header-close-icon {
      background-image: url("images/icon_close.png");
      background-repeat: no-repeat;
      background-position: 0 center;
      position: absolute;
      right: -30px;
      top: -3px;
      z-index: 101;
      cursor: pointer;
      width: 24px;
      height: 100%;
      display: inline-block;

      &:hover {
        background-image: url("images/icon_close_hover.png");
      }
    }
  }

  .drag-dialog__body {
    width: 100%;
    padding: 16px 16px 0 16px;
  }

  .dialog-footer {
    width: 100%;
    position: relative;
    background-color: transparent;
    text-align: right;
    padding: 32px 0 12px;

    ::v-deep .el-button {
      margin-right: 12px;
    }
  }
}

.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}
</style>
